#ifndef __BUOLA_GUI_X11_CX11DISPATCHER_H__
#define __BUOLA_GUI_X11_CX11DISPATCHER_H__

#include <buola/gui/x11/auto.h>

#include <X11/Xlib.h>
#include <X11/extensions/shape.h>
#include <X11/extensions/Xdamage.h>
#include <buola/io/cfdlistener.h>
#include <buola/app/ceventloop.h>

#include <unordered_map>

namespace buola { namespace gui { namespace x11 {

class CX11EventFilter
{
public:
    virtual ~CX11EventFilter() {}

    virtual bool OnX11Event(XEvent*)                        {   return true;    }
    virtual bool OnX11ClientMessage(XClientMessageEvent*)   {   return true;    }
    virtual bool OnX11ShapeEvent(XShapeEvent*)              {   return true;    }
    virtual bool OnX11DamageEvent(XDamageNotifyEvent*)      {   return true;    }
    virtual bool OnX11BadWindow(Window)             {   return true;    }
};

class CX11Dispatcher : public UUncopiable,public CLoopFunction,public io::CFDListener
{
public:
    enum EOptions
    {
        OPTION_NONE=0,
        OPTION_PROCESSALL=(1<<0)
    };

    DECLARE_FRIEND_ENUM_BITOPS(EOptions)

    CX11Dispatcher(CX11Display*,CX11Toolkit*);

    static int ErrorHandler(Display*,XErrorEvent*);
    static int IOErrorHandler(Display*);

public:
///////////////////////
// event dispatching
//
    static EMouseButton TranslateButtonFromX(unsigned int);
    static unsigned int TranslateButtonToX(EMouseButton);

    void DispatchEvent(XEvent*);
    void DispatchNextEvent();

    void AddFilter(CX11EventFilter*);
    void RemoveFilter(CX11EventFilter*);
    void AddClientMessageFilter(CX11EventFilter*);
    void RemoveClientMessageFilter(CX11EventFilter*);
    void AddDamageFilter(CX11EventFilter*);
    void RemoveDamageFilter(CX11EventFilter*);
    void AddExternalWindowFilter(Window,CX11EventFilter*);
    void RemoveExternalWindowFilter(Window,CX11EventFilter*);

    void SetOptions(EOptions pOptions);

private:
    void DispatchExposeEvent(XExposeEvent*);
    void DispatchGraphicsExposeEvent(XGraphicsExposeEvent*);
    void DispatchKeyEvent(XKeyEvent*,bool=false);
    void DispatchButtonEvent(XButtonEvent*);
    void DispatchMotionEvent(XMotionEvent*);
    void DispatchCrossingEvent(XCrossingEvent*);
    void DispatchConfigureEvent(XConfigureEvent*);
    void DispatchFocusEvent(XFocusChangeEvent*);
    void DispatchFilterEvent(XEvent*);
    void DispatchShapeEvent(XEvent*);
    void DispatchDamageEvent(XEvent*);
    void DispatchCreateEvent(XCreateWindowEvent*);
    void DispatchDestroyEvent(XDestroyWindowEvent*);
    void DispatchMapEvent(XMapEvent*);
    void DispatchUnmapEvent(XUnmapEvent*);
    void DispatchSelectionRequestEvent(XSelectionRequestEvent*);
    void DispatchSelectionClearEvent(XSelectionClearEvent*);
    void DispatchSelectionNotifyEvent(XSelectionEvent*);
    void DispatchClientMessageEvent(XClientMessageEvent*);
    void DispatchAlarm(XEvent*);
    bool DispatchBadWindow(Window pID);

    void DispatchPending(CX11Window*,int);

    virtual bool OnLoopFunction();
    virtual void OnFDListener(int,io::ENotify,void*);

//////////
// data
//
    std::vector<CX11EventFilter*> mFilters;
    std::vector<CX11EventFilter*> mClientMessageFilters;
    std::vector<CX11EventFilter*> mDamageFilters;
    std::unordered_map<TID,CX11EventFilter*> mExternalWindowFilters;

    CX11Toolkit *mToolkit;
    CX11Display *mDisplay;

    EOptions mOptions;

//double-click control
    CX11Window *mLastClickWindow;
    CPoint_d mLastClickPos;
    EMouseButton mLastClickButton;
    int mLastClickTime; //!< in milliseconds, as in X11 events
    int mLastClickCount;

    friend class CX11Window;
};

/*namespace x11*/ } /*namespace gui*/ } /*namespace buola*/ }

#endif
